﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AvaliacaoOnline.Data;
using System.Data;
using AvaliacaoOnline.Model;

namespace AvaliacaoOnline.Model
{
    public class AvaliacaoRepository
    {
        public List<Avaliacao> GetAvaliacoes(int idCliente)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();
            return context.Avaliacao.Where(c => c.IdCliente == idCliente).OrderByDescending(c => c.DataFinal).ToList();
        }

        public List<Avaliacao> GetAvaliacoesUsuario(int idUsuario)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();

            DateTime dataInicioBase = DateTime.Now.NormalizeDate().AddMinutes(3);
            DateTime dataFinalBase = DateTime.Now.NormalizeDate().AddMinutes(-3);

            return (from a in context.Avaliacao.Include("AvaliacaoUsuario")
                    join u in context.AvaliacaoUsuario on a.Id equals u.IdAvaliacao
                    where u.IdUsuario == idUsuario &&
                          ((a.DataInicial <= dataInicioBase &&
                           a.DataFinal >= dataFinalBase) || a.ExibirResultado == "S")
                    select a
                   ).ToList();
        }

        public List<Avaliacao> GetProximasAvaliacoesUsuario(int idUsuario)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();

            DateTime dataInicioBase = DateTime.Now.NormalizeDate().AddMinutes(3);
            DateTime dataFinalBase = DateTime.Now.NormalizeDate().AddMinutes(-3);

            return (from a in context.Avaliacao.Include("AvaliacaoUsuario")
                    join u in context.AvaliacaoUsuario on a.Id equals u.IdAvaliacao
                    where u.IdUsuario == idUsuario && a.DataFinal >= dataFinalBase
                    orderby a.DataInicial
                    select a
                   ).ToList();
        }

        public List<Avaliacao> GetAvaliacoesGestor(int idCliente, int idUsuario)
        {
            //UsuarioRepository oUsuarioRep = new UsuarioRepository();
            //Usuario usuario = oUsuarioRep.GetUsuario(idUsuario);



            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();
            List<Avaliacao> lstAval = (from a in context.Avaliacao.Include("AvaliacoesUsuario.Usuario")
                                       where a.IdCliente == idCliente
                                       select a
                                       ).ToList();

            List<int> lstIdAvaliacaoDoUsuario = GetAvaliacaoUsuario(idUsuario).Select(c => c.IdAvaliacao).ToList();

            //return lstAval.Where(c => c.AvaliacoesUsuario.Where(u => u.Usuario.IdGrupoUsuario == usuario.IdGrupoUsuario).Count() > 0).ToList();
            return lstAval.Where(c => lstIdAvaliacaoDoUsuario.Contains(c.Id)).ToList();
        }

        public AvaliacaoUsuario GetAvaliacaoUsuario(int idUsuario, int idAvaliacao)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();
            return (from a in context.AvaliacaoUsuario
                    where a.IdUsuario == idUsuario &&
                          a.IdAvaliacao == idAvaliacao
                    select a
                   ).FirstOrDefault();
        }

        public List<AvaliacaoUsuario> GetAvaliacaoUsuario(int idUsuario)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();
            return (from a in context.AvaliacaoUsuario
                    where a.IdUsuario == idUsuario
                    select a
                   ).ToList();
        }

        public Avaliacao GetAvaliacao(int idAvaliacao)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();
            return context.Avaliacao.Where(c => c.Id == idAvaliacao).FirstOrDefault();
        }

        public List<Questao> GetQuestoes(int idAvaliacao)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();

            return context.Questao.Include("Avaliacao").Include("AlternativasQuestao")
                .OrderBy(c => c.Numero)
                .Where(c => c.IdAvaliacao == idAvaliacao).ToList();
        }

        public List<Questao> GetQuestoes(int idUsuario, int idAvaliacao)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();

            if (context.AvaliacaoUsuario.Where(c => c.IdAvaliacao == idAvaliacao && c.IdUsuario == idUsuario).Count() == 0)
            {
                return new List<Questao>();
            }

            var lstQuestoes = context.Questao.Include("Avaliacao").Include("AlternativasQuestao")
                .OrderBy(c => c.Numero)
                .Where(c => c.IdAvaliacao == idAvaliacao).ToList();

            if (lstQuestoes.Count != 0)
            {
                //Aleatorio
                byte[] b = new byte[1];
                if (lstQuestoes.First().Avaliacao.QuestoesAleatorias == "S")
                    lstQuestoes = lstQuestoes
                        .OrderBy(c => c.Nivel)
                        .ThenBy(c => CryptoService.ComputeHash("TextoHashAleatorio#$¨%&*¨sdgf" + idUsuario.ToString() + (c.Id + 3210).ToString() + (c.Numero + 1000).ToString(), "MD5", b))
                        .ToList();
                else
                    lstQuestoes = lstQuestoes.OrderBy(c => c.Numero).ToList();

                string esquema = lstQuestoes.First().Avaliacao.EsquemaQuestoes;
                if (!String.IsNullOrWhiteSpace(esquema))
                {
                    List<Questao> lstNovas = new List<Questao>();

                    foreach (string item in esquema.Split('/'))
                    {
                        int qtde = int.Parse(item.Split('x')[0]);
                        int nivel = int.Parse(item.Split('x')[1]);
                        lstNovas.AddRange(lstQuestoes.Where(c => c.Nivel == nivel).Take(qtde));
                        lstNovas = lstNovas
                            .OrderBy(c => c.Nivel)
                            .OrderBy(c => CryptoService.ComputeHash("TextoHashAleatorio#$¨%&*¨sdgf" + idUsuario.ToString() + (c.Id + 3210).ToString() + (c.Numero + 1000).ToString(), "MD5", b))
                            .ToList();
                    }
                    return lstNovas;
                }
            }

            return lstQuestoes;
        }

        public void GravarAvaliacao(Avaliacao avaliacao, ref string erros)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();

            avaliacao.LimiteTempo = avaliacao.LimiteTempo ?? string.Empty;
            avaliacao.ImagemFundo = avaliacao.ImagemFundo ?? string.Empty;
            avaliacao.ArquivoEstilo = avaliacao.ArquivoEstilo ?? string.Empty;
            avaliacao.MensagemConclusao = avaliacao.MensagemConclusao ?? string.Empty;
            avaliacao.MensagemParticipacao = avaliacao.MensagemParticipacao ?? string.Empty;
            avaliacao.Resumo = avaliacao.Resumo ?? string.Empty;
            avaliacao.ExibirResultado = avaliacao.ExibirResultado ?? "N";

            List<String> lstErros = ValidaAvaliacao(avaliacao).ToList();
            if (lstErros.Count > 0)
            {
                foreach (string s in lstErros)
                    erros += s + Environment.NewLine;

                return;
            }


            if (avaliacao.Id == 0)
            {
                context.Avaliacao.AddObject(avaliacao);
            }
            else
            {
                context.Avaliacao.Attach(avaliacao);
                context.ObjectStateManager.ChangeObjectState(avaliacao, EntityState.Modified);
            }
            context.SaveChanges();
        }

        public void ExcluirAvaliacao(int idAvaliacao, ref string erros)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();

            if (context.AvaliacaoUsuario
                .Where(c => c.IdAvaliacao == idAvaliacao && c.DataInicial != null).Count() > 0)
            {
                erros = "Esta avaliação já foi iniciada por um ou mais usuário e não pode ser excluída.";
                return;
            }

            foreach (var item in context.Questao.Where(c => c.IdAvaliacao == idAvaliacao).ToList())
                this.ExcluirQuestao(context, item, ref erros);

            if (!String.IsNullOrEmpty(erros))
                return;

            var lstAvaliacaoUsuario = context.Questao.Where(c => c.IdAvaliacao == idAvaliacao).ToList();

            context.DeleteObject(context.Avaliacao.Where(c => c.Id == idAvaliacao).First());

            context.SaveChanges();
        }

        public void AtualizarImagemFundo(int IdAvaliacao, string imagemFundo)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();
            Avaliacao avaliacao = context.Avaliacao.Where(c => c.Id == IdAvaliacao).FirstOrDefault();
            avaliacao.ImagemFundo = imagemFundo;
            context.SaveChanges();
        }

        public void AtualizarArquivoEstilo(int IdAvaliacao, string arquivoEstilo)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();
            Avaliacao avaliacao = context.Avaliacao.Where(c => c.Id == IdAvaliacao).FirstOrDefault();
            avaliacao.ArquivoEstilo = arquivoEstilo;
            context.SaveChanges();
        }

        public string GravarQuestoes(int idAvaliacao, List<QuestaoModelo> lstModelos)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();
            StringBuilder builder = new StringBuilder();

            foreach (QuestaoModelo q in lstModelos)
            {
                Questao questao = new Questao();
                questao.IdAvaliacao = idAvaliacao;
                questao.Numero = int.Parse(q.Numero);
                questao.Descricao = q.Texto;
                questao.IdCategoriaQuestao = 1; //default
                questao.Nivel = q.Nivel;

                QuestaoOpcaoModelo opcaoCorreta = q.Opcoes.Where(c => c.Correta).First();
                if (opcaoCorreta != null)
                    questao.NumeroAlternativaCorreta = int.Parse(opcaoCorreta.Numero);

                foreach (QuestaoOpcaoModelo qo in q.Opcoes)
                {
                    questao.AlternativasQuestao.Add(new AlternativaQuestao()
                    {
                        Descricao = qo.Texto,
                        NumeroAlternativa = int.Parse(qo.Numero)
                    });
                }

                if (context.Questao.Where(c => c.IdAvaliacao == idAvaliacao && c.Numero == questao.Numero).Count() > 0)
                {
                    builder.AppendLine("Questão número " + questao.Numero + " já existe.");
                }
                else
                {
                    context.Avaliacao.Where(c => c.Id == idAvaliacao).First().Questoes.Add(questao);
                    builder.AppendLine("Questão número " + questao.Numero + " inserida com sucesso.");
                }
            }

            context.SaveChanges();
            return builder.ToString();
        }

        public void ExcluirQuestao(int idQuestao, ref string erros)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();

            this.ExcluirQuestao(context, context.Questao.Where(c => c.Id == idQuestao).First(), ref erros);

            context.SaveChanges();
        }

        public void ExcluirAlternativa(int idQuestao, int numeroAlternativa, ref string erros)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();

            AlternativaQuestao item = context.AlternativaQuestao.Where(c => c.IdQuestao == idQuestao && c.NumeroAlternativa == numeroAlternativa).First();
            context.DeleteObject(item);

            context.SaveChanges();
        }

        private void ExcluirQuestao(AvaliacaoOnlineContext context, Questao questao, ref string erros)
        {
            foreach (Resposta item in context.Resposta.Where(c => c.IdQuestao == questao.Id))
                context.DeleteObject(item);

            foreach (AlternativaQuestao item in context.AlternativaQuestao.Where(c => c.IdQuestao == questao.Id))
                context.DeleteObject(item);

            context.DeleteObject(questao);
        }

        public void AtualizarDescricaoQuestao(int idQuestao, int numeroAlternativa, string descricao, int correta)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();
            if (numeroAlternativa == 0)
            {
                Questao questao = context.Questao.Where(c => c.Id == idQuestao).First();
                questao.Descricao = descricao;
            }
            else
            {
                AlternativaQuestao alternativaQuestao;
                if (numeroAlternativa != 9999)
                {
                    alternativaQuestao = context.AlternativaQuestao.Where(c => c.IdQuestao == idQuestao && c.NumeroAlternativa == numeroAlternativa).First();
                    alternativaQuestao.Descricao = descricao;
                }
                else
                {
                    var ultimaQuestao = context.AlternativaQuestao.Where(c => c.IdQuestao == idQuestao).OrderByDescending(c => c.NumeroAlternativa).First();
                    alternativaQuestao = new AlternativaQuestao();
                    alternativaQuestao.IdQuestao = idQuestao;
                    alternativaQuestao.Descricao = descricao;
                    if (ultimaQuestao == null)
                        alternativaQuestao.NumeroAlternativa = 1;
                    else
                        alternativaQuestao.NumeroAlternativa = ultimaQuestao.NumeroAlternativa + 1;

                    context.AlternativaQuestao.AddObject(alternativaQuestao);
                }

                if (correta == 1)
                {
                    Questao questao = context.Questao.Where(c => c.Id == idQuestao).First();
                    questao.NumeroAlternativaCorreta = alternativaQuestao.NumeroAlternativa;
                }
            }
            context.SaveChanges();
        }

        public void AtualizarArquivoQuestao(int idQuestao, int tipo, string nomeArquivo)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();

            var questao = context.Questao.Where(c => c.Id == idQuestao).First();

            if (tipo == 1)
                questao.ArquivoMidia1 = nomeArquivo;
            else if (tipo == 2)
                questao.ArquivoMidia2 = nomeArquivo;
            else if (tipo == 3)
                questao.ArquivoMidia3 = nomeArquivo;

            context.SaveChanges();
        }

        public IEnumerable<String> ValidaAvaliacao(Avaliacao avaliacao)
        {
            if (String.IsNullOrWhiteSpace(avaliacao.Titulo))
                yield return "O campo titulo não pode ser vazio.";
            if (String.IsNullOrWhiteSpace(avaliacao.Descricao))
                yield return "O campo descrição não pode ser vazio.";
            if (String.IsNullOrWhiteSpace(avaliacao.Resumo))
                yield return "O campo resumo não pode ser vazio.";

            if (avaliacao.LimiteTempo == "S" && avaliacao.QuantidadeSegundos == 0)
                yield return "O campo quantidade em segundos não pode ser 0 caso a avaliação tenha limite de tempo.";

            if (avaliacao.DataFinal.CompareTo(avaliacao.DataInicial) < 0)
                yield return "A data final não pode ser menor que a data inicial.";
        }

        public List<Resposta> GetRespostas(int idUsuario, int idAvaliacao)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();

            var resposta = context.Resposta.Include("AlternativaQuestao.Questao")
                .Where(c => c.IdUsuario == idUsuario &&
                c.IdAvaliacao == idAvaliacao
                ).ToList();

            return resposta;
        }

        public int GetResposta(int idUsuario, int idAvaliacao, int idQuestao)
        {
            AvaliacaoOnlineContext context = AvaliacaoOnlineContext.GetContext();

            var resposta = context.Resposta.Where(c => c.IdUsuario == idUsuario &&
                c.IdAvaliacao == idAvaliacao &&
                c.IdQuestao == idQuestao).FirstOrDefault();

            if (resposta == null)
                return -1;

            return resposta.NumeroAlternativa;
        }
    }
}
